#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
#include <map>
#include <set>
#include <assert.h>
#include <queue>
#include <cstring>

typedef std::pair<int, int> Grid;
const int H = 128, W = 128, safe_distance = 1; // size of layout and safe distace
int is_blocked[H][W]; // 0 means not blocked, 1 means blocked, 2 means blocked by two droplets, and so on 
/* add or remove block */
void update_block_grid(Grid grid, int status) {
    for (int i = std::max(grid.first - safe_distance, 1); i <= std::min(grid.first + safe_distance, W); i++)
        for (int j = std::max(grid.second - safe_distance, 1); j <= std::min(grid.second + safe_distance, H); j++) {
            is_blocked[i][j] += status;
        }
}

class Droplet {
    public: 
    Grid start, end;
    int height, width;
    Grid current;
    // path is the routing path from start to end, without considering the timing
    std::vector<Grid> path;
    // path_sync is the routing path from start to end, considering the timing
    std::vector<Grid> path_sync;
    // the index of current position in path and droplet id
    int current_path_index, id;
    int priority;
    // the check_point is the index of path_sync to indicate that path_sync[check_point] is a location to resurrect when crushed
    // the check_point_index is the index of path
    int check_point, check_point_index;

    Droplet(Grid start, Grid end, int id, int height, int width) {
        this->start = start;
        this->end = end;
        this->id = id;
        this->current = start;
        this->height = height;
        this->width = width;
    }

    bool operator < (Droplet& droplet) const {
        return id < droplet.id;
    }

    bool operator == (Droplet& droplet) const {
        return start == droplet.start;
    }
};

std::map<int, std::shared_ptr<Droplet>> id_to_droplet_mapping;

// Node represents a grid in the layout
class Node {
    public:
    std::multiset<int> contamination; // the trace of droplet

    // phantom_block is the set of phantom droplet that occupy the grid
    std::multiset<std::pair<int, int>> phantom_block;
    void add_phantom_block(std::shared_ptr<Droplet> droplet, int priority) {
        phantom_block.insert({priority, droplet -> id});
    }
    void remove_phantom_block(std::shared_ptr<Droplet> droplet, int priority) {
        phantom_block.erase(find(phantom_block.begin(), phantom_block.end(), std::make_pair(priority, droplet -> id)));
    }
    void add_contamination(int priority) {
        contamination.insert(priority);
    }
    void remove_contamination(int priority) {
        contamination.erase(find(contamination.begin(), contamination.end(), priority));
    }
    int min_contamination_priority() {
        return *contamination.begin();
    }
    int min_phantom_block_priority() {
        return phantom_block.begin() -> first;
    }
}node[H][W];

// add or remove phantom block
void add_phantom_block(std::shared_ptr<Droplet> droplet, Grid grid, bool status, int priority, int drop_height, int drop_width) {
    for (int i = std::max(grid.first - safe_distance, 1); i < std::min(grid.first + drop_height + safe_distance, W); i++)
        for (int j = std::max(grid.second - safe_distance, 1); j < std::min(grid.second + drop_width + safe_distance, H); j++) {
            if (status)
                node[i][j].add_phantom_block(droplet, priority);
            else 
                node[i][j].remove_phantom_block(droplet, priority);
        }
}

// undo the path from check_point to time_now
void resurrect(std::shared_ptr<Droplet> droplet, int time_now) {
    for (int i = droplet -> check_point; i <= time_now; i++) {
        droplet -> path_sync[i] = droplet -> path_sync[droplet -> check_point];
    }
    add_phantom_block(droplet, droplet -> current, false, droplet -> priority, droplet -> height, droplet -> width);
    droplet -> current = droplet -> path_sync[droplet -> check_point];
    droplet -> current_path_index = droplet -> check_point_index;
    add_phantom_block(droplet, droplet -> current, true, droplet -> priority, droplet -> height, droplet -> width);
}

// clear the phantom block that has higher priority than the current priority
void clear_phantom(Node& node, int priority) {
    auto copy = node.phantom_block;
    for (auto& i: copy) {
        auto droplet = id_to_droplet_mapping[i.second];
        if (i.first > priority) {
            resurrect(droplet, int(droplet -> path_sync.size()) - 1);
        }
    }
}

// check if there is a phantom block with lower priority than the current priority
bool check_phatom_block(int priority, Grid& grid, int drop_height, int drop_width) {
    for (int i = grid.first; i < grid.first + drop_height; i++)
        for (int j = grid.second; j < grid.second + drop_width; j++) {
            if (node[i][j].phantom_block.size() && node[i][j].min_phantom_block_priority() < priority)
                return true;
        }
    return false;
}

// check if there is a contamination with lower priority than the current priority
bool check_contamination(int priority, Grid& grid, int drop_height, int drop_width) {
    for (int i = grid.first; i < grid.first + drop_height; i++)
        for (int j = grid.second; j < grid.second + drop_width; j++) {
            if (node[i][j].contamination.size() && node[i][j].min_contamination_priority() < priority)
                return true;
        }
    return false;
}

// calculate the number of contamination in the grid
int calc_contamination(Grid& grid, int drop_height, int drop_width) {
    int cnt = 0;
    for (int i = grid.first; i < grid.first + drop_height; i++)
        for (int j = grid.second; j < grid.second + drop_width; j++) {
            cnt += node[i][j].contamination.size();
        }
    return cnt;
}

void update_contamination(Grid& grid, bool status, int priority, int drop_height, int drop_width) {
    for (int i = std::max(grid.first - safe_distance, 1); i < std::min(grid.first + drop_height + safe_distance, W); i++)
        for (int j = std::max(grid.second - safe_distance, 1); j < std::min(grid.second + drop_width + safe_distance, H); j++) {
            if (status) {
                node[i][j].add_contamination(priority);
            } else {
                node[i][j].remove_contamination(priority);
            }
        }
}

void update_check_point(std::shared_ptr<Droplet> droplet) {
    auto last_check_point = droplet -> path_sync[droplet -> check_point];
    for (int i = droplet -> check_point_index; i < droplet -> current_path_index; i++) {
        update_contamination(droplet -> path[i], false, droplet -> priority, droplet -> height, droplet -> width);
    }
    add_phantom_block(droplet, last_check_point, false, droplet -> priority, droplet -> height, droplet -> width);
    add_phantom_block(droplet, droplet -> current, true, droplet -> priority, droplet -> height, droplet -> width);
    droplet -> check_point = int(droplet -> path_sync.size()) - 1;
    droplet -> check_point_index = droplet -> current_path_index;
}

void move_one_step(std::shared_ptr<Droplet> droplet, int drop_height, int drop_width) {
    auto next_position = droplet -> path[droplet -> current_path_index + 1];
    for (int i = next_position.first; i < next_position.first + drop_height; i++)
        for (int j = next_position.second; j < next_position.second + drop_width; j++) {
            clear_phantom(node[i][j], droplet -> priority);
        }
    droplet -> current_path_index++;
    add_phantom_block(droplet, next_position, true, droplet -> priority, drop_height, drop_width);
    droplet -> path_sync.push_back(next_position);
    droplet -> current = droplet -> path_sync.back();
}
void update_state(std::shared_ptr<Droplet> droplet) {
    auto last_position = droplet -> path_sync[int(droplet -> path_sync.size()) - 2];
    add_phantom_block(droplet, last_position, false, droplet -> priority, droplet -> height, droplet -> width);
}
std::vector<std::shared_ptr<Droplet>> drop_lets;

bool is_area_blocked(Grid& grid, int drop_height, int drop_width) {
    bool blocked = false;
    for (int i = grid.first; i < grid.first + drop_height; i++)
        for (int j = grid.second; j < grid.second + drop_width; j++) {
            blocked |= (is_blocked[i][j] > 0);
        }
    return blocked;
}

// search for closest droplet routing path from start to end, use A* algorithm and bypass the blocked area
// path contamination number is the second priority
bool visited[H][W];
// contamination is the number of path that pass through the grid
int dist[H][W], contamination[H][W]; 
Grid pre[H][W];
int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
bool path_search(std::shared_ptr<Droplet> droplet, Grid end_point, int priority) {
    // 设置当前的液滴当前位置为-1
    update_block_grid(droplet -> current, -1);
    std::queue<Grid> q;
    q.push(droplet -> current);
    memset(visited, false, sizeof(visited));
    memset(dist, 0x3f, sizeof(dist));
    dist[droplet -> current.first][droplet -> current.second] = 0;
    visited[droplet -> current.first][droplet -> current.second] = true;
    while (!q.empty()) {
        Grid cur = q.front();
        q.pop();
        if (cur == end_point) {
            Grid now = cur;
            while (now != droplet -> current) {
                droplet -> path.push_back(now);
                update_contamination(now, true, priority, droplet -> height, droplet -> width);
                now = pre[now.first][now.second];
            }
            update_contamination(now, true, priority, droplet -> height, droplet -> width);
            droplet -> path.push_back(droplet -> current);
            reverse(droplet -> path.begin(), droplet -> path.end());
            update_block_grid(end_point, 1);
            droplet -> current = end_point;
            droplet -> priority = priority;
            return true;
        }
        for (int i = 0; i < 4; i++) {
            Grid next = {cur.first + dx[i], cur.second + dy[i]};
            if (next.first < 1 || next.first + droplet -> height > H || next.second < 1 || next.second + droplet -> width > W) {
                continue;
            }
            if (dist[next.first][next.second] == dist[cur.first][cur.second] + 1) {
                if (calc_contamination(next, droplet -> height, droplet -> width) + contamination[cur.first][cur.second] < contamination[next.first][next.second]) {
                    contamination[next.first][next.second] = calc_contamination(next, droplet -> height, droplet -> width) + contamination[cur.first][cur.second];
                    pre[next.first][next.second] = cur;
                }
            }
            if (visited[next.first][next.second]) {
                continue;
            }
            if (is_area_blocked(next, droplet -> height, droplet -> width)) {
                continue;
            }
            visited[next.first][next.second] = true;
            dist[next.first][next.second] = dist[cur.first][cur.second] + 1;
            pre[next.first][next.second] = cur;
            q.push(next);
        }
    }
    update_block_grid(droplet -> current, 1);
    return false;
}

int reset_time = 0;

void reset_order() {
    Grid center = {H / 2, W / 2};
    std::sort(drop_lets.begin(), drop_lets.end(), [&](std::shared_ptr<Droplet> a, std::shared_ptr<Droplet> b) {
        return abs(a -> end.first - center.first) + abs(a -> end.second - center.second) > abs(b -> end.first - center.first) + abs(b -> end.second - center.second);
    });
}

void reset_spacing() {
    std::vector<int> r_axis;
    for (auto& droplet: drop_lets) {
        r_axis.push_back(droplet -> current.first);
    }
    // 求横坐标集合
    std::sort(r_axis.begin(), r_axis.end());
    r_axis.erase(std::unique(r_axis.begin(), r_axis.end()), r_axis.end());
    reset_time = r_axis.size();
    // 求横坐标集合的中间位置的值为基准
    int base = (r_axis.size() + 1) / 2;
    int base_row = r_axis[base - 1];
    int Ceil = 0;
    for (auto drop: drop_lets) {
        update_block_grid(drop -> current, -1);
    }
    // 求每个液滴到（0，drop->current.second）最大未被阻碍的横坐标
    for (auto drop: drop_lets) {
        int tmp_Ceil = 0;
        for (int i = drop -> current.first; i >= 0; i--) {
            if (is_blocked[i][drop -> current.second]) {
                break;
            }
            tmp_Ceil = i;
        }
        Ceil = std::max(Ceil, tmp_Ceil);
    } 
    base_row = std::max(Ceil + (base - 1) * 4, base_row);
    std::vector<int> offset(r_axis.size());
    for (int i = 0; i < int(r_axis.size()); i++) {
        offset[i] = (i - base + 1) * 4 + base_row - r_axis[i];
        // std::clog << offset[i] << " ";
        reset_time = std::max(reset_time, abs(offset[i]) + 1);
    }
    for (auto drop: drop_lets) {
        int offset_row = offset[std::lower_bound(r_axis.begin(), r_axis.end(), drop -> current.first) - r_axis.begin()];
        if (offset_row < 0) {
            for (int i = 0; i < abs(offset_row); i++) {
                drop -> path_sync.push_back({drop -> current.first - i, drop -> current.second});
            }
        } else {
            for (int i = 0; i < abs(offset_row); i++) {
                drop -> path_sync.push_back({drop -> current.first + i, drop -> current.second});
            }
        }
        drop -> current = {drop -> current.first + offset_row, drop -> current.second};
        drop -> start = drop -> current;
        for (int i = abs(offset_row) + 1; i <= reset_time; i++) {
            drop -> path_sync.push_back(drop -> current);
        }
        // std::clog << drop -> current.first << " " << drop -> current.second << std::endl;
    }
    for (auto drop: drop_lets) {
        update_block_grid(drop -> current, 1);
    }
}

void set_priority() {
    int cnt = 0;
    auto tmp_drop_lets = drop_lets;
    while(tmp_drop_lets.size()) {
        cnt++;
        int num = 0;
        // find the feasible droplet
        while(path_search(tmp_drop_lets[num], tmp_drop_lets[num] -> end, cnt) == false && num < int(tmp_drop_lets.size())) num++;
        tmp_drop_lets.erase(tmp_drop_lets.begin() + num, tmp_drop_lets.begin() + num + 1);
    }
}

// concurrent routing for all droplets, if droplet step into a grid which droplet with higher priority will pass, it is racing
// if not crush, then no racing, else the droplet with lower priority will be routed to the safe grid
// means that during this period, it is stalling and waiting for the droplet with higher priority to pass in the safe grid
// pay attention to the safe distance
bool check_finished() {
    for (auto i: drop_lets) {
        if (i -> current_path_index != int(i -> path.size()) - 1) {
            return false;
        }
    }
    return true;
}


void concurrent_routing() {
    const int MAX_TIME = 128 * 4 * drop_lets.size();
    // initialize 
    for (auto& i: drop_lets) {
        update_block_grid(i -> current, -1);
        add_phantom_block(i, i -> start, true, i -> priority, i -> height, i -> width);
        add_phantom_block(i, i -> start, true, i -> priority, i -> height, i -> width);
        i -> current = *i -> path.begin();
        i -> check_point = i -> path_sync.size();
        i -> check_point_index = 0;
        i -> path_sync.push_back(i -> current);
    }
    sort(drop_lets.begin(), drop_lets.end(), [](std::shared_ptr<Droplet> a, std::shared_ptr<Droplet> b) {
        return a -> priority < b -> priority;
    });
    for (int time = 1; time <= MAX_TIME; time++) {
        if (check_finished()) {
            break;
        }
        for (auto& droplet: drop_lets) {
            // if (droplet -> current_index == int(droplet -> path.size()) - 1) continue;
            int current_index = droplet -> current_path_index;
            Grid next_positon = current_index == int(droplet -> path.size()) - 1 ? droplet -> end : droplet -> path[current_index + 1];
            if (droplet -> current_path_index == int(droplet -> path.size()) - 1 
            || check_phatom_block(droplet -> priority, next_positon, droplet -> height, droplet -> width)) {
                // stalling
                droplet -> path_sync.push_back(droplet -> current);
                add_phantom_block(droplet, droplet -> current, true, droplet -> priority, droplet -> height, droplet -> width);
            } else {
                move_one_step(droplet, droplet -> height, droplet -> width);
            }
        }
        for (auto& droplet: drop_lets) {
            // if (droplet -> current_index == int(droplet -> path.size()) - 1) continue;
            update_state(droplet);
            if (check_contamination(droplet -> priority, droplet -> current, droplet -> height, droplet -> width) == false) {
                update_check_point(droplet);
            }
        }
    }
}

// print the location of droplet and obstacles for every second in ./debug folder
void print_path(int t) {
    std::clog << "error in time " << t << std::endl;
    std::string file_name = "./debug.txt";
    freopen(file_name.c_str(), "w", stdout);
    std::cout << drop_lets.size() << std::endl;
    for (auto& droplet: drop_lets) {
        std::cout << droplet -> path_sync[t].first << " " << droplet -> path_sync[t].second << std::endl;
        std::cout << droplet -> path_sync[t - 1].first << " " << droplet -> path_sync[t - 1].second << std::endl;
    }
    //obstacle print
    std::vector<Grid> obstacle;
    for (int i = 1; i <= H; i++)
        for (int j = 1; j <= W; j++) {
            if (is_blocked[i][j]) {
                obstacle.push_back({i, j});
            }
        }
    std::cout << obstacle.size() << std::endl;
    for (auto i: obstacle) {
        std::cout << i.first << " " << i.second << std::endl;
    }
}

// check if the distance between two droplets is less than safe distance
bool check_dis(Grid& a, Grid& b, int drop_height = 1, int drop_width = 1) {
    for (int i = a.first; i < a.first + drop_height; i++)
        for (int j = a.second; j < a.second + drop_width; j++)
            for (int k = b.first; k < b.first + drop_height; k++)
                for (int l = b.second; l < b.second + drop_width; l++) {
                    int dis1 = abs(i - k);
                    int dis2 = abs(j - l);
                    if (dis1 <= safe_distance && dis2 <= safe_distance) {
                        return false;
                    }
                }
    return true;
}

void check(int la) {
    std::vector<Grid> last_position;
    for (auto i: drop_lets) {
        last_position.push_back(i -> path_sync[0]);
        int lim = abs(i -> path_sync[0].first - i -> end.first) + abs(i -> path_sync[0].second - i -> end.second);
        lim *= 2;
        for (auto j: i -> path_sync) {
            if (is_blocked[j.first][j.second]) assert(0);
        }
        for (int j = 1; j < int(i -> path_sync.size()); j++) {
            if (i -> path_sync[j] != i -> path_sync[j - 1]) lim--;
        }
    }
    for (int i = 1; i<= la; i++) {
        std::vector<Grid> now_position;
        for (auto j: drop_lets) {
            now_position.push_back(j -> path_sync[i]);
            if (is_blocked[j -> path_sync[i].first][j -> path_sync[i].second]) {
                std::clog << "error in checking" << std::endl;
                print_path(i);
                assert(false);
            }
        }
        for (int j = 0; j < int(drop_lets.size()); j++) 
            for (int k = j + 1; k < int(drop_lets.size()); k++) {
                if(!check_dis(now_position[j], now_position[k])) {
                    std::clog << "error in checking" << std::endl;
                    std::clog << i << " " << j << " " << k << std::endl;
                    std::clog << last_position[j].first << " " << last_position[j].second << std::endl;
                    std::clog << now_position[k].first << " " << now_position[k].second << std::endl;
                    print_path(i);
                    assert(false);
                }
                if (!check_dis(now_position[j], last_position[k])) {
                    std::clog << "error in checking" << std::endl;
                    std::clog << i << " " << j << " " << k << std::endl;
                    std::clog << last_position[j].first << " " << last_position[j].second << std::endl;
                    std::clog << now_position[k].first << " " << now_position[k].second << std::endl;
                    print_path(i);
                    assert(false);
                }
                if (!check_dis(last_position[j], now_position[k])) {
                    std::clog << "error in checking" << std::endl;
                    std::clog << i << " " << j << " " << k << std::endl;
                    std::clog << last_position[j].first << " " << last_position[j].second << std::endl;
                    std::clog << now_position[k].first << " " << now_position[k].second << std::endl;
                    print_path(i);
                    assert(false);
                }
            }
        last_position = now_position;
    }
    std::clog << "check finished!" << std::endl;
}

void print_ans() {
    int La = 0;
    // sort by id
    std::sort(drop_lets.begin(), drop_lets.end(), [](std::shared_ptr<Droplet> a, std::shared_ptr<Droplet> b) {
        return a -> id < b -> id;
    });
    for (auto& droplet: drop_lets) {
        La = std::max(La, int(droplet -> path_sync.size()) - 1);
    }
    std::clog << "Last arrival time:" << La << std::endl;
    int cnt = 0;
    for (auto& droplet: drop_lets) {
        std::cout << "drop" << ++cnt << ":";
        for (auto grid: droplet -> path_sync) {
            if (grid == droplet -> end) continue;
            std::cout << "(" << grid.first << "," << grid.second << ")";
            std::cout << "(" << droplet -> width << "," << droplet -> height << ");";
        }
        std::cout << "(" << droplet -> end.first << "," << droplet -> end.second << ")";
        std::cout << "(" << droplet -> width << "," << droplet -> height << ");" << std::endl;
    }
    #ifdef _DEBUG
    check(La);
    #endif
}


int main() {
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    int n;
    std::cin >> n;
    std::vector<Grid> start_points, end_points;
    std::vector<std::pair<int, int>> drop_size;
    for (int i = 1; i <= n; i++) {
        Grid start;
        int drop_height, drop_width;
        std::cin >> start.first >> start.second >> drop_height >> drop_width;
        start_points.push_back(start);
        drop_size.push_back({drop_height, drop_width});
    }
    std::cin >> n;
    for (int i = 1; i <= n; i++) {
        Grid end;
        int drop_height, drop_width;
        std::cin >> end.first >> end.second >> drop_height >> drop_width;
        end_points.push_back(end);
    }
    int id = 0;
    for (int i = 1; i <= n; i++) {
        update_block_grid(start_points[i - 1], 1);
        auto droplet = std::make_shared<Droplet>(start_points[i - 1], end_points[i - 1], ++id, drop_size[i - 1].first, drop_size[i - 1].second);
        drop_lets.push_back(droplet);
        id_to_droplet_mapping[id] = drop_lets.back();
    }
    int m;
    std::cin >> m;
    for (int i = 1; i <= m; i++) {
        int x, y, z, w;
        std::cin >> x >> y >> z >> w;
        for (int j = x; j < x + z; j++)
            for (int k = y; k < y + w; k++) {
                update_block_grid((Grid){j, k}, 999);
            }
    }
    reset_spacing();
    reset_order();
    set_priority();
    concurrent_routing();
    print_ans();
}